package com.wang.seckill.controller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ramostear.captcha.HappyCaptcha;
import com.ramostear.captcha.common.Fonts;
import com.ramostear.captcha.support.CaptchaStyle;
import com.ramostear.captcha.support.CaptchaType;
import com.wang.seckill.config.AccessLimit;
import com.wang.seckill.pojo.Order;
import com.wang.seckill.pojo.SeckillMessage;
import com.wang.seckill.pojo.SeckillOrder;
import com.wang.seckill.pojo.User;
import com.wang.seckill.rabbitmq.MQSenderMessage;
import com.wang.seckill.service.GoodsService;
import com.wang.seckill.service.OrderService;
import com.wang.seckill.service.SeckillOrderService;
import com.wang.seckill.vo.GoodsVo;
import com.wang.seckill.vo.RespBean;
import com.wang.seckill.vo.RespBeanEnum;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

    @Resource
    private GoodsService goodsService;

    @Resource
    private SeckillOrderService seckillOrderService;

    @Resource
    private OrderService orderService;

    @Resource
    private RedisTemplate redisTemplate;

    private HashMap<Long, Boolean> entryStockMap = new HashMap<>();

    @Resource
    private MQSenderMessage mqSenderMessage;

    @RequestMapping("/{path}/doSeckill")
    @ResponseBody
    public RespBean doSeckill(@PathVariable String path, Model model, User user, Long goodsId) {


        if (user == null) {//用户没有登录
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

        //这里我们增加一个判断逻辑，校验用户携带的path是否正确
        boolean b = orderService.checkPath(user, goodsId, path);
        if (!b) {//校验失败
            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
        }

        //获取到goodsVo
        GoodsVo goodsVo = goodsService.findGoodsVoById(goodsId);

        //判断库存
        if (goodsVo.getStockCount() < 1) {//没有库存
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }


        //判断用户是否复购-直接到Redis中,获取对应的秒杀订单,如果有,则说明已经抢购了
        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue()
                .get("order:" + user.getId() + ":" + goodsVo.getId());
        if (null != o) { //说明该用户已经抢购了该商品
            return RespBean.error(RespBeanEnum.REPEAT_ERROR);
        }

        //对map进行判断[内存标记],如果商品在map已经标记为没有库存，则直接返回，无需进行Redis预减
        if (entryStockMap.get(goodsId)) {
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }

        //库存预减, 如果在Redis中预减库存，发现秒杀商品已经没有了，就直接返回
        //从而减少去执行 orderService.seckill() 请求,防止线程堆积, 优化秒杀/高并发
        //老师提示: decrement()是具有原子性[!!]
        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
        if (decrement < 0) {//说明这个商品已经没有库存

            //说明当前秒杀的商品，已经没有库存
            entryStockMap.put(goodsId, true);

            //恢复库存为0
            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
        }

        //抢购,向消息队列发送秒杀请求,实现了秒杀异步请求
        //这里我们发送秒杀消息后，立即快速返回结果[临时结果] - "比如排队中.."
        //客户端可以通过轮询，获取到最终结果
        //创建SeckillMessage
        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
        return RespBean.error(RespBeanEnum.SEK_KILL_WAIT);

    }

    //方法: 获取秒杀路径,使用自定义注解完成用户的限流
    @RequestMapping("/path")
    @ResponseBody
    @AccessLimit(second = 5, maxcount = 5, needLogin = true)
    public RespBean getPath(User user, Long goodsId, String captcha, HttpServletRequest request) {
        if (user == null || goodsId < 0) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }

//        //增加业务逻辑：加入redis计数器，完成限流
//        String uri = request.getRequestURI();
//        ValueOperations valueOperations = redisTemplate.opsForValue();
//        String key = uri + ":" + user.getId();
//        Integer count = (Integer) valueOperations.get(key);
//        if (count == null) {
//            valueOperations.set(key, 1, 5, TimeUnit.SECONDS);
//        } else if (count < 5) {
//            valueOperations.increment(key);
//        } else {
//            return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REACHED);
//        }

        //增加一个业务逻辑-校验用户输入的验证码是否正确
        boolean check = orderService.checkCaptcha(user, goodsId, captcha);
        if (!check) {//如果校验失败
            return RespBean.error(RespBeanEnum.CAPTCHA_ERROR);
        }

        String path = orderService.createPath(user, goodsId);
        return RespBean.success(path);

    }

    @RequestMapping("/captcha")
    public void happyCaptcha(HttpServletRequest request, HttpServletResponse response, User user, Long goodsId) {
        //生成验证码并输出，保存到session中
        HappyCaptcha.require(request, response)
                .style(CaptchaStyle.ANIM)
                .type(CaptchaType.NUMBER_ZH_CN)
                .length(6).width(220).height(80)
                .font(Fonts.getInstance().zhFont())
                .build().finish();

//        验证码保存到redis
        redisTemplate.opsForValue().set("captcha:" + user.getId() + ":" +goodsId,
                (String) request.getSession().getAttribute("happy-captcha"), 100, TimeUnit.SECONDS);

    }




    //该方法是在SeckillController类/对象的所有属性，都是初始化后，自动执行的
    //这里我们就可以将所有秒杀商品的库存量，加载到Redis
    @Override
    public void afterPropertiesSet() throws Exception {

        //查询所有的秒杀商品
        List<GoodsVo> list = goodsService.findGoodsVo();
        //先判断是否为空
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        //遍历List,然后将秒杀商品的库存量，放入到Redis
        //秒杀商品库存量对应key : seckillGoods:商品id
        list.forEach(goodsVo -> {


            redisTemplate.opsForValue()
                    .set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());

            //初始化map
            //如果goodsId : false 表示有库存
            //如果goodsId : true 表示没有库存
            entryStockMap.put(goodsVo.getId(), false);

        });

    }
}

//    @RequestMapping("/{path}/doSeckill")
//    public RespBean doSeckill(@PathVariable String path, Model model, User user, Long goodsId) {
//
//        if (user == null) {
////            return "login";
//            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//        }
//
//        //增加逻辑，携带路径是否正确
//        boolean b = orderService.checkPath(user, goodsId, path);
//        if (!b) {
//            return RespBean.error(RespBeanEnum.REQUEST_ILLEGAL);
//        }
//
////        model.addAttribute("user", user);
//
//        //获取goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoById(goodsId);
//
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//        //判断用户是否在复购
//        SeckillOrder o = (SeckillOrder) redisTemplate.opsForValue().get("order:" + user.getId() + ":" + goodsVo.getId());
//        if (o != null) {
////            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.REPEAT_ERROR);
//        }
//
//        //对map进行判断
//        if (entryStockMap.get(goodsId)) {
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//
//        //预减库存，减少db压力
//        Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
//        if (decrement < 0) {
//            entryStockMap.put(goodsId, true);
//            //恢复库存为0
//            redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
////            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
////            return "secKillFail";
//            return RespBean.error(RespBeanEnum.ENTRY_STOCK);
//        }
//
//        //抢购向消息队列发送秒杀请求
//        //这里发送秒杀消息后，立即快速返回结果
//        //客户段通过轮询获得结果
//        SeckillMessage seckillMessage = new SeckillMessage(user, goodsId);
//        mqSenderMessage.sendSeckillMessage(JSONUtil.toJsonStr(seckillMessage));
////        model.addAttribute("errmsg", "排队中。。。");
////        return "secKillFail";
//        return RespBean.error(RespBeanEnum.SEK_KILL_WAIT);
//
//    }
//
//    //该方法是在类的所有属性都初始化之后，自动执行的
//    @Override
//    public void afterPropertiesSet() throws Exception {
//        //查询所有秒杀商品
//        List<GoodsVo> list = goodsService.findGoodsVo();
//        if (CollectionUtils.isEmpty(list)) {
//            return;
//        }
//
//        list.forEach(goodsVo -> {
//            redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());
//            //goodsId:false 有库存
//            //goodsId:true 没有库存
//            entryStockMap.put(goodsVo.getId(), false);
//        });
//
//    }
//
//    //获取秒杀路径
//    @RequestMapping("/path")
//    @ResponseBody
//    public RespBean getPath(User user, Long goodsId) {
//
//        if (user == null || goodsId < 0) {
//            return RespBean.error(RespBeanEnum.SESSION_ERROR);
//        }
//
//        String path = orderService.createPath(user, goodsId);
//        return RespBean.success(path);
//    }

    //处理用户抢购请求
//    @RequestMapping("/doSeckill")
//    public String doSeckill(Model model, User user, Long goodsId) {
//        System.out.println("1.0");
//
//        if (user == null) {
//            return "login";
//        }
//
//        model.addAttribute("user", user);
//
//        //获取goodsVo
//        GoodsVo goodsVo = goodsService.findGoodsVoById(goodsId);
//
//        //判断库存
//        if (goodsVo.getStockCount() < 1) {
//            model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK);
//            return "seckillFail";
//        }
//
//        //判断用户是否在复购
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>().eq("user_id", user.getId()).eq("goods_id", goodsId));
//        if (seckillOrder != null) {
//            model.addAttribute("errmsg", RespBeanEnum.REPEAT_ERROR);
//            return "seckillFail";
//        }
//
//        //抢购
//        Order order = orderService.seckill(user, goodsVo);
//        if (order == null) {
//            model.addAttribute("errmsg",RespBeanEnum.ENTRY_STOCK);
//            return "seckillFail";
//        }
//
//        model.addAttribute("order", order);
//        model.addAttribute("goods", goodsVo);
//
//
//        System.out.println("1.0");
//        return "orderDetail";
//    }


